package com.arpa.wms.service.impl;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.entity.Shipment;
import com.arpa.ntocc.common.service.IShipmentService;
import com.arpa.wms.cache.ImportTempCache;
import com.arpa.wms.cache.UnitCache;
import com.arpa.wms.common.util.UUIDUtil;
import com.arpa.wms.domain.consts.PropertyConst;
import com.arpa.wms.domain.dto.GoodsUnitDTO;
import com.arpa.wms.domain.dto.GoodsUnitExcelDTO;
import com.arpa.wms.domain.entity.GoodsUnit;
import com.arpa.wms.domain.enums.DeleteFlagEnum;
import com.arpa.wms.domain.enums.StatusEnum;
import com.arpa.wms.domain.vo.GoodsUnitExcelVO;
import com.arpa.wms.domain.vo.UnitVO;
import com.arpa.wms.mapper.GoodsUnitMapper;
import com.arpa.wms.service.IGoodsUnitService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.qimen.api.request.ItemsSynchronizeRequest;
import com.qimen.api.request.SingleitemSynchronizeRequest;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;

/**
 * @author: Allen chen
 * @Date 2020/8/11
 * @Description:
 */
@Service
@Log4j2
public class GoodsUnitServiceImpl extends ServiceImpl<GoodsUnitMapper, GoodsUnit> implements IGoodsUnitService {

    @Autowired
    private ShipmentCache shipmentCache;
    private final PartyCache partyCache;

    private final UnitCache unitCache;

    @Autowired
    private ImportTempCache importTempCache;

    @Autowired
    private IShipmentService shipmentService;

    private static final String GOODS_UNIT_CODE = "单位编码不能重复";
    private static final String GOODS_UNIT_NAME = "单位名称不能重复";

    public GoodsUnitServiceImpl(PartyCache partyCache, UnitCache unitCache) {
        this.partyCache = partyCache;
        this.unitCache = unitCache;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(GoodsUnit entity) {
        entity.setStatus(StatusEnum.UNACTIVE.getValue());
        if (StrUtil.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.simpleUUID());
        }
        entity.setGroupCode(UserUtil.getBranchCode());
        if (StrUtil.isBlank(entity.getCreatedBy())) {
            entity.setCreatedBy(UserUtil.getCode());
            entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
        }
        entity.setModifiedBy(UserUtil.getCode());
        entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        if(StrUtil.isBlank(entity.getShipmentName())) {
            entity.setShipmentName(shipmentCache.translate(entity.getShipmentCode()));
        }

        GoodsUnitDTO temp = new GoodsUnitDTO();
        temp.setUnitName(entity.getUnitName());
        temp.setShipmentCode(entity.getShipmentCode());
        temp.setGroupCode(UserUtil.getBranchCode());
        //校验单位名称和编码是否重复
        UnitVO unitVO = baseMapper.checkNameOrCode(temp);
        if (null != unitVO) {
            throw new ServiceException(GOODS_UNIT_NAME);
        }
        temp.setUnitName("");
        temp.setUnitCode(entity.getUnitCode());
        UnitVO unitVo1 = baseMapper.checkNameOrCode(temp);
        if (null != unitVo1) {
            throw new ServiceException(GOODS_UNIT_CODE);
        }

        unitCache.flushAllCacheByGroupCode(UserUtil.getBranchCode());
        unitCache.flushACacheByShipmentCode(temp.getShipmentCode());


        return super.save(entity);
    }

    @Override
    public int updateByCode(GoodsUnit entity) {
        //修改的时候，单位名称和单位编码如果不能修改，则不进行校验
      /*  GoodsUnitDTO temp = new GoodsUnitDTO();
        temp.setUnitName(entity.getUnitName());
        temp.setShipmentCode(entity.getShipmentCode());
        UnitVO unitVO = baseMapper.checkNameOrCode(temp);
        if (null != unitVO && !unitVO.getCode().equals(entity.getCode())) {
            throw new ServiceException("单位名称已存在！");
        }

        temp.setUnitName("");
        temp.setUnitCode(entity.getUnitCode());
        UnitVO unitVO1 = baseMapper.checkNameOrCode(temp);
        if (null != unitVO1 && !unitVO1.getCode().equals(entity.getCode())) {
            throw new ServiceException("单位编码已存在！");
        }*/
        if (StrUtil.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }

        unitCache.flushCache(entity.getCode());
        return baseMapper.update(entity, new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getCode, entity.getCode()).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
    }

    @Override
    public List<UnitVO> queryList(GoodsUnitDTO entrty) {

        if (StrUtil.isNotEmpty(entrty.getSortField())) {
            entrty.setSortField(CommonUtil.camel2Underline(entrty.getSortField()));
            if ("status_name".equals(entrty.getSortField())){
                entrty.setSortField("a.status");
            }
        }
        List<UnitVO> unitVoS= baseMapper.queryList(entrty);

        if (IterUtil.isNotEmpty(unitVoS)) {
            unitVoS.forEach(r -> {
                if(StrUtil.isBlank(r.getShipmentName())) {
                    r.setShipmentName(shipmentCache.translate(r.getShipmentCode()));
                }
                StatusEnum enumByValue = StatusEnum.getEnumByValue(r.getStatus());
                if (null != enumByValue) {
                    r.setStatusName(enumByValue.getDesc());
                }
            });
        }

        return unitVoS;
    }

    @Override
    public UnitVO queryListSum(GoodsUnitDTO entrty) {
        UnitVO unitVO = baseMapper.queryListSum(entrty);
        return unitVO;
    }

    @Override
    public int batchDelete(List<String> list) {

        return baseMapper.batchDelete(list);
    }

    private int updateStatus(List<String> codes, String status){
        int num = 0;
        boolean flag = false;
        if(status.equals(StatusEnum.ACTIVE.getValue())){
            flag = this.update(new UpdateWrapper<GoodsUnit>().lambda().set(GoodsUnit::getStatus, status).set(GoodsUnit::getDeleteFlag, DeleteFlagEnum.NOTDELETE.getCode().toString()).in(GoodsUnit::getCode, codes).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
        }else {
            flag = this.update(new UpdateWrapper<GoodsUnit>().lambda().set(GoodsUnit::getStatus, status).in(GoodsUnit::getCode, codes).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
        }
        if (flag) {
            for (String code : codes) {
                unitCache.flushCache(code);
                num++;
            }
        }
        return num;
    }

    @Override
    public Map<String,Object> batchUpdateStatus(List<String> codes, String status) {
        Map<String,Object> result = new HashMap<>();
       if(status.equals(StatusEnum.UNACTIVE.getValue())){


           List<Map<String,Integer>> useCount =  baseMapper.goodsUseCount(codes);
           Map<Boolean, List<String>> codesMap = codes.stream().collect(Collectors.groupingBy(r -> (useCount.stream().filter(c->StrUtil.equals(MapUtil.getStr(c,"unitCode"),r)).findFirst()).isPresent()));
           List<String> notUse = codesMap.get(false);
           List<String> use = codesMap.get(true);
           if (IterUtil.isNotEmpty(notUse)) {
               updateStatus(notUse, status);
               result.put(PropertyConst.SUCCESS, notUse.size());
           }
           if (IterUtil.isNotEmpty(use)) {
               result.put("fail", use.size());
               List<String> message = use.stream().map(r ->unitCache.translate(r)).collect(Collectors.toList());
               result.put("msg", message);
           }





       }else {
           int num = updateStatus(codes, status);
           result.put(PropertyConst.SUCCESS,num);
       }
        return result;
    }

    @Override
    public List<UnitVO> queryUnitForGoods(GoodsUnitDTO goodsUnit) {

        return baseMapper.queryUnitForGoods(goodsUnit);
    }

    @Override
    public List<UnitVO> queryUnitsByCodes(List<String> codes) {

        return baseMapper.queryUnitsByCodes(codes);
    }

    /**
     * 获取货主商品单位列表
     *
     * @param shipmentCode 货主code
     * @return
     */
    @Override
    public List<Map<String, Object>> findUnitByShipmentCode(String shipmentCode) {
        List<Map<String, Object>> units = unitCache.getAllByShipmentCode(shipmentCode);
        //对未启用单位进行过滤
        if (IterUtil.isNotEmpty(units)) {
            units = units.stream().filter(r -> NUM_ZERO.toString().equals(r.get("status"))).collect(Collectors.toList());
        }
        return units;
    }

    /**
     * 通过code删除单位
     *
     * @param codes
     */
    @Override
    public Map<String,Object> removeByCode(List<String> codes) {
        Map<String,Object> result = new HashMap<>();

        List<UnitVO> unitVoS = queryUnitsByCodes(codes);
        List<String> notDelete = unitVoS.stream().filter(info -> info.getDeleteFlag().intValue() == DeleteFlagEnum.NOTDELETE.getCode().intValue()).map(r -> {
            return r.getCode();
        }).collect(Collectors.toList());
        if(IterUtil.isNotEmpty(notDelete)){
            List<String> notDeleteNames = notDelete.stream().map(code -> {
                return unitCache.translate(code);
            }).collect(Collectors.toList());
            result.put("notDelete",notDeleteNames);
        }
        List<String> canDelete = codes.stream().filter(r -> !notDelete.contains(r)).collect(Collectors.toList());
        int delete = 0;
        if(IterUtil.isNotEmpty(canDelete)){
            delete = baseMapper.delete(new QueryWrapper<GoodsUnit>().lambda().in(GoodsUnit::getCode, canDelete).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
            if(delete >= 0){
                for (String code:canDelete) {
                    unitCache.flushCache(code);
                }
            }
        }
        result.put(PropertyConst.SUCCESS,delete);
        return result;
    }

    @Override
    public List<Map<String, Object>> search(String keywords, String shipmentCode) {
        List<Map<String, Object>> units = unitCache.getAllByShipmentCode(shipmentCode);
        if (IterUtil.isNotEmpty(units)) {
            if (StrUtil.isNotBlank(keywords)) {
                // 根据关键词过滤商品单位
                units = units.stream().filter(r ->
                        StrUtil.containsIgnoreCase(MapUtil.getStr(r, "name"), keywords)
                ).collect(Collectors.toList());
            }
        }
        return units;
    }

    /**
     * 通过code获取商品单位
     *
     * @param code
     * @return
     */
    @Override
    public GoodsUnit getOneByCode(String code) {
        GoodsUnit one = baseMapper.selectOne(new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getCode, code));
        one.setShipmentName(shipmentCache.translate(one.getShipmentCode()));
        return one;
    }

    private void validateNotBlank(List<GoodsUnitExcelDTO> list){

        IntStream.range(NUM_ZERO, list.size()).forEach(index -> {
            // 校验Excel里必填项是否有空值
            if (StrUtil.isEmpty(list.get(index).getShipmentSerialNumber())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_BLANK);
            }
            if (StrUtil.isEmpty(list.get(index).getUnitCode())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + UNIT_CODE + NOT_BLANK);
            }
            if (StrUtil.isEmpty(list.get(index).getUnitName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + UNIT_NAME + NOT_BLANK);
            }
        });
    }

    public void validateRepeatData(List<GoodsUnitExcelDTO> list){
        List<GoodsUnitExcelDTO> tempList = list;
        Stream.iterate(NUM_ZERO, index -> index + NUM_ONE).limit(list.size()).forEach(index -> {
            Stream.iterate(NUM_ZERO, indexJ -> indexJ + NUM_ONE).limit(tempList.size()).forEach(indexJ -> {
                boolean shipmentSerialNumberFlag = list.get(index).getShipmentSerialNumber().equals(tempList.get(indexJ).getShipmentSerialNumber());
                boolean unitCodeFlag = list.get(index).getUnitCode().equals(tempList.get(indexJ).getUnitCode());
                if(shipmentSerialNumberFlag && unitCodeFlag && !index.equals(indexJ)) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + AND_NAME + TIP_PREFIX + (indexJ + NUM_THREE) + LINE + REPEAT_DATA);
                }
            });
        });
    }

    @Override
    public List<GoodsUnitExcelVO> goodsUnitExcelView(List<GoodsUnitExcelDTO> list) {
        //必填项是否为空
        //校验重复数据
        validateNotBlank(list);
        validateRepeatData(list);
        String importCode = IdUtil.simpleUUID();
        List<Shipment> shipmentList = shipmentService.list();
        List<GoodsUnitExcelVO> finalList = IntStream.range(NUM_ZERO, list.size()).mapToObj(index -> {
            //判断货主是否存在
            GoodsUnitExcelVO vo = new GoodsUnitExcelVO();
            GoodsUnitExcelDTO indexVo = list.get(index);
            BeanUtils.copyProperties(indexVo,vo);
            List<Shipment> shipments = shipmentList.stream().filter(info -> indexVo.getShipmentSerialNumber().equals(info.getPartyCode())).collect(Collectors.toList());
            if (IterUtil.isEmpty(shipments)) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_EXIST);
            }
            Map<String, Shipment> shipmetMap = shipmentList.stream().collect(Collectors.toMap(Shipment::getPartyCode, Function.identity()));
            if(!shipmetMap.isEmpty()){
                Shipment ship = shipmetMap.get(indexVo.getShipmentSerialNumber());
                if(null != ship){
                    vo.setShipmentName(ship.getUserName());
                    vo.setShipmentCode(ship.getPartyCode());
                }
            }
            vo.setImportCode(importCode);
            return vo;
        }).collect(Collectors.toList());
        // 保存到缓存中
        importTempCache.putTempData(importCode, finalList);
        return finalList;
    }

    private void importSaveOrUpdate(GoodsUnitExcelVO goodsUnit){

        GoodsUnit one = this.getOne(new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getUnitCode, goodsUnit.getUnitCode()).eq(GoodsUnit::getShipmentCode,goodsUnit.getShipmentCode()).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
        if(null == one){
            GoodsUnit save = new GoodsUnit();
            save.setCode(UUIDUtil.getUUID32());
            save.setGroupCode(UserUtil.getBranchCode());
            save.setShipmentCode(goodsUnit.getShipmentCode());
            save.setShipmentName(goodsUnit.getShipmentName());
            save.setUnitName(goodsUnit.getUnitName());
            save.setUnitCode(goodsUnit.getUnitCode());
            save.setRemarks(goodsUnit.getRemarks());
            save.setStatus(StatusEnum.UNACTIVE.getValue());
            save.setCreatedBy(UserUtil.getCode());
            save.setCreatedName(partyCache.translate(UserUtil.getCode()));
            save.setModifiedBy(UserUtil.getCode());
            save.setModifiedName(partyCache.translate(UserUtil.getCode()));
            this.save(save);
        }else {
            LambdaUpdateWrapper<GoodsUnit> update = new UpdateWrapper<GoodsUnit>().lambda();
            update.eq(GoodsUnit::getCode,one.getCode());
            update.eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode());
            update.eq(GoodsUnit::getShipmentCode,one.getShipmentCode());
            update.set(GoodsUnit::getUnitName, goodsUnit.getUnitName());
            update.set(GoodsUnit::getModifiedBy,UserUtil.getCode());
            update.set(GoodsUnit::getGmtModified, LocalDateTime.now());
            update.set(GoodsUnit::getModifiedName,partyCache.translate(UserUtil.getCode()));
            if(StringUtils.isNotEmpty(goodsUnit.getRemarks())){
                update.set(GoodsUnit::getRemarks, goodsUnit.getRemarks());
            }
            this.update(update);
        }

    }

    @Override
    public Result importUnits(String importCode) {

        // 根据导入码，获取导入缓存
        List<GoodsUnitExcelVO> tempData = importTempCache.getTempData(importCode, GoodsUnitExcelVO.class);
        // 判断数据，如果没有了，则直接返回
        if(tempData == null){
            return Result.error("缓存数据不存在，或超过有效时间，缓存已被清除，请重新导入。");
        }
        // 遍历保存
        tempData.forEach(e->{
            // 保存或更新数据
            importSaveOrUpdate(e);
        });
        List<String> shipmentCodes = tempData.stream().map(info -> {
            return info.getShipmentCode();
        }).collect(Collectors.toList());
        List<String> distinct = shipmentCodes.stream().distinct().collect(Collectors.toList());
        // 完成导入后删除导入的缓存
        importTempCache.removeCache(importCode);
        // 删除原单位缓存
        distinct.stream().forEach(code ->{
            unitCache.flushACacheByShipmentCode(code);
        });

        return Result.ok();
    }

    /**
     * 批量同步商品时，根据操作类型判断是插入还是更新
     *
     * @param request
     * @param shipmentName
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void batchGoodsUnitSave(ItemsSynchronizeRequest request, String shipmentName, Map<String,String> unitMap){
        String shipmentPartyCode = request.getOwnerCode();
        List<ItemsSynchronizeRequest.Item> items = request.getItems();
        List<GoodsUnit> goodsUnitList = Lists.newArrayList();
        for (ItemsSynchronizeRequest.Item item : items) {
            String unitName = item.getStockUnit();
            //根据货主code和单位名称查询单位信息
            GoodsUnit originGoodsUnit = this.getOne(new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getShipmentCode, shipmentPartyCode).eq(GoodsUnit::getUnitName, unitName));
            if (ObjectUtil.isNull(originGoodsUnit)) {
                GoodsUnit goodsUnit = new GoodsUnit();
                goodsUnit.setCode(IdUtil.simpleUUID());
                goodsUnit.setShipmentCode(shipmentPartyCode);
                goodsUnit.setShipmentName(shipmentName);
                goodsUnit.setUnitName(item.getStockUnit());
                goodsUnit.setCreatedBy(UserUtil.getCode());
                goodsUnit.setCreatedName(partyCache.translate(UserUtil.getCode()));
                unitMap.put(shipmentPartyCode + "_" + unitName, goodsUnit.getCode());
                goodsUnitList.add(goodsUnit);
            }
        }

        // 拼接sql，批量执行新增操作
        importBatchSave(goodsUnitList);
    }

    /**
     * 同步单个商品时，根据操作类型判断是插入还是更新
     *
     * @param request
     * @param shipmentName
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void singleGoodsUnitSave(SingleitemSynchronizeRequest request, String shipmentName, Map<String,String> unitMap){
        log.info("《《《《《 同步商品单位开始 1》》》》》");
        String unitName = request.getItem().getStockUnit();
        String shipmentPartyCode = request.getOwnerCode();
        //同步过来的单位名称为空时，不能进行商品同步
        if (StrUtil.isEmpty(unitName)) {
            log.info("商品单位为空，不能进行商品同步");
            throw new ServiceException("商品单位为空，不能进行商品同步");
        }
        //根据货主code和单位名称查询单位信息
        GoodsUnit originGoodsUnit = this.getOne(new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getShipmentCode, shipmentPartyCode).eq(GoodsUnit::getUnitName, unitName).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));

        if (ObjectUtil.isNull(originGoodsUnit)) {
            GoodsUnit goodsUnit = new GoodsUnit();
            goodsUnit.setCode(IdUtil.simpleUUID());
            goodsUnit.setShipmentCode(shipmentPartyCode);
            goodsUnit.setShipmentName(shipmentName);
            goodsUnit.setUnitName(request.getItem().getStockUnit());
            goodsUnit.setCreatedBy(UserUtil.getCode());
            goodsUnit.setCreatedName(partyCache.translate(UserUtil.getCode()));
            unitMap.put(shipmentPartyCode + "_" + unitName, goodsUnit.getCode());
            this.save(goodsUnit);
        }
        log.info("《《《《《 同步商品单位开结束 1》》》》》");
    }

    /**
     * 同步-批量保存操作
     * @param goodsUnitList
     * @return
     */
    private int importBatchSave(List<GoodsUnit> goodsUnitList) {
        StringBuilder data = new StringBuilder();
        data.append("INSERT INTO `base_unit`(`id`, `code`,`group_code`, `shipment_code`,`shipment_name`, "
                + " `unit_name`,`gmt_created`, `created_name`) values ");
        String sql = goodsUnitList.stream().map(e -> makeInsertValueStr(e)).collect(Collectors.joining(","));
        data.append(sql);
        return this.baseMapper.execSql(data.toString());
    }

    /**
     * 同步-批量插入生成sql
     * @param goodsUnit
     * @return
     */
    private StringBuilder makeInsertValueStr(final GoodsUnit goodsUnit) {
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append("default, ");
        // code
        data.append("'").append(goodsUnit.getCode()).append("', ");
        // group_code
        data.append("'").append(UserUtil.getBranchCode()).append("', ");
        // shipment_code
        data.append("'").append(ObjectUtil.defaultIfNull(goodsUnit.getShipmentCode(), StrUtil.EMPTY)).append("', ");
        // shipment_name
        data.append("'").append(ObjectUtil.defaultIfNull(goodsUnit.getShipmentName(), StrUtil.EMPTY)).append("', ");
        // unit_name
        data.append("'").append(ObjectUtil.defaultIfNull(goodsUnit.getUnitName(), StrUtil.EMPTY)).append("', ");
        // gmt_created
        data.append("'").append(goodsUnit.getGmtCreated()).append("', ");
        // gmt_modified
        data.append("'").append(goodsUnit.getCreatedName()).append("' ");
        data.append(")");
        return data;
    }

}
